package monitor

import (
	"fmt"
	"gitee.com/aldrice/monitor/ent"
	"gitee.com/aldrice/monitor/ent/metric"
	"gitee.com/aldrice/monitor/ent/object"
	"gitee.com/aldrice/monitor/ent/setting"
	"gitee.com/aldrice/monitor/ent/user"
	"gitee.com/aldrice/monitor/internal/router"
	"strconv"
)

type createSettingReq struct {
	Nickname string `json:"nickname" validate:"required"`
	Length   int    `json:"length" validate:"required"`
	Method   string `json:"method" validate:"required"`
	IsEnable string `json:"is_enable" validate:"required"`

	Metric   string `json:"metric" validate:"required"`
	ObjectID int    `json:"object_id" validate:"required"`
}

var (
	errInvalidObjectID  = router.ApiError(12003, "invalid object id")
	errInvalidSettingID = router.ApiError(12004, "invalid setting id")
)

// CreateSetting
// @Summary Setting Create
// @Description 用户创建数据聚合设置
// @ID monitor-setting-create
// @Tags setting
// @Accept json
// @Produce json
// @Router /monitor/setting [post]
func CreateSetting(c *router.Context) error {
	req := new(createSettingReq)
	if err := c.Bind(req); err != nil {
		return err
	}
	if err := c.Validate(req); err != nil {
		return err
	}

	ctx, db, uid := c.Ctx(), c.DB(), c.UID()

	// 首先检查，当前用户是否拥有id为req.ObjectID的object
	_, err := db.Object.Query().Where(
		object.IDEQ(req.ObjectID),
		object.HasOwnedByWith(user.IDEQ(uid)),
	).First(ctx)
	if err != nil {
		if ent.IsNotFound(err) {
			return errInvalidObjectID
		}
		return err
	}

	mid, err := db.Metric.Query().Where(metric.NameEqualFold(req.Metric)).FirstID(ctx)
	if err != nil {
		return err
	}

	// 若以上流程无问题，则保存该设置
	s, err := db.Setting.Create().
		SetNickname(req.Nickname).
		SetLength(req.Length).
		SetMethod(setting.Method(req.Method)).
		SetWithMetricID(mid).
		SetWithObjectID(req.ObjectID).
		SetEnable(req.IsEnable == "true").
		Save(ctx)
	if err != nil {
		return err
	}

	return c.ApiSuccess(s.ID)
}

type editSettingReq struct {
	Nickname string `json:"nickname" validate:"required"`
	Length   int    `json:"length" validate:"required"`
	Method   string `json:"method" validate:"required"`
	Metric   string `json:"metric" validate:"required"`
	IsEnable string `json:"is_enable" validate:"required"`

	ID int `json:"id" validate:"required"`
}

// EditSetting
// @Summary User Edit it's setting
// @Description 用户修改数据聚合配置
// @ID monitor-setting-edit
// @Tags setting
// @Accept json
// @Produce json
// @Router /monitor/setting [put]
func EditSetting(c *router.Context) error {
	req := new(editSettingReq)
	if err := c.Bind(req); err != nil {
		return err
	}
	if err := c.Validate(req); err != nil {
		return err
	}

	ctx, db, uid := c.Ctx(), c.DB(), c.UID()

	// 首先，先查询用户所描述的setting是否属于他
	s, err := db.Setting.Query().Where(
		setting.IDEQ(req.ID),
		setting.HasWithObjectWith(object.HasOwnedByWith(user.IDEQ(uid))),
	).First(ctx)
	if err != nil {
		if ent.IsNotFound(err) {
			return errInvalidSettingID
		}
		return err
	}

	mid, err := db.Metric.Query().Where(metric.NameEqualFold(req.Metric)).FirstID(ctx)
	if err != nil {
		return err
	}

	_, err = s.Update().
		SetNickname(req.Nickname).
		SetLength(req.Length).
		SetMethod(setting.Method(req.Method)).
		SetEnable(req.IsEnable == "true").
		SetWithMetricID(mid).
		Save(ctx)
	if err != nil {
		return err
	}

	return c.ApiSuccess(nil)
}

type getSettingsRespItem struct {
	ent.Setting
	Metric   string `json:"metric"`
	IsEnable string `json:"is_enable"`
}

// GetSettings
// @Summary Get settings
// @Description 用户获取聚合数据集
// @ID monitor-settings-get
// @Tags setting
// @Accept json
// @Produce json
// @Router /monitor/settings [get]
func GetSettings(c *router.Context) error {
	db, ctx := c.DB(), c.Ctx()

	sQry := db.Setting.Query()

	if c.QueryParam("object_id") != "" {
		oid, err := strconv.Atoi(c.QueryParam("object_id"))
		if err != nil {
			return err
		}
		_, err = db.User.Query().Where(user.IDEQ(c.UID()), user.HasHaveObjectWith(object.IDEQ(oid))).First(ctx)
		if err != nil {
			return err
		}

		sQry.Where(setting.HasWithObjectWith(object.IDEQ(oid))).WithWithMetric()
	}

	cnt, err := sQry.Clone().Count(ctx)
	if err != nil {
		return err
	}
	if cnt == 0 {
		return c.ApiTable(0, nil)
	}

	page, limit := c.DeterminePage()
	ord := c.DetermineOrder(setting.Columns)
	objects, err := sQry.Limit(limit).Offset(page).Order(ord).All(ctx)
	if err != nil {
		return err
	}

	items := make([]getSettingsRespItem, len(objects))
	for i, s := range objects {
		items[i] = getSettingsRespItem{
			Setting:  *s,
			Metric:   fmt.Sprintf("%s (ID: %d)", s.Edges.WithMetric.Name, s.Edges.WithMetric.ID),
			IsEnable: strconv.FormatBool(s.Enable),
		}
	}

	return c.StdTable(cnt, items)
}

// GetSetting
// @Summary Get setting
// @Description 用户获取聚合设置
// @ID monitor-setting-get
// @Tags setting
// @Accept param
// @Produce json
// @Router /monitor/setting [get]
func GetSetting(c *router.Context) error {
	sid, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		return err
	}

	st, err := c.DB().Setting.Query().Where(
		setting.HasWithObjectWith(object.HasOwnedByWith(user.IDEQ(c.UID()))),
		setting.IDEQ(sid),
	).WithWithMetric().First(c.Ctx())
	if err != nil {
		return err
	}

	return c.ApiSuccess(getSettingsRespItem{
		Setting:  *st,
		Metric:   st.Edges.WithMetric.Name,
		IsEnable: fmt.Sprintf("%t", st.Enable)},
	)
}

// DeleteSetting
// @Summary Delete settings
// @Description 删除聚合设置
// @ID monitor-setting-delete
// @Tags setting
// @Accept json
// @Produce json
// @Router /monitor/setting [delete]
func DeleteSetting(c *router.Context) error {
	sID, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		return err
	}

	_, err = c.DB().Setting.Delete().Where(
		setting.IDEQ(sID),
		setting.HasWithObjectWith(object.HasOwnedByWith(user.IDEQ(c.UID()))),
	).Exec(c.Ctx())
	if err != nil {
		return err
	}
	return c.ApiSuccess(nil)
}

type method struct {
	Name setting.Method `json:"name"`
}

// GetMethods
// @Summary Get setting methods
// @Description 删除聚合设置
// @ID monitor-setting-method-get
// @Tags setting
// @Accept json
// @Produce json
// @Router /monitor/setting/methods [delete]
func GetMethods(c *router.Context) error {
	return c.ApiSuccess([]method{
		{Name: setting.MethodMean},
		{Name: setting.MethodSum},
		{Name: setting.MethodMax},
		{Name: setting.MethodMin},
		{Name: setting.MethodNone},
	})
}
